#!/bin/sh

# Grade all of the submissions for assignment 1
# Please see the README.txt file for setup instructions

MSBUILD_EXE="/cygdrive/c/Windows/Microsoft.NET/Framework/v4.0.30319/MSBuild.exe" 	# MSBuild executable
MINICAP_EXE="/cygdrive/c/Program Files (x86)/MiniCap/MiniCap.exe"
VS_EXE="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 10.0/Common7/IDE/VCExpress.exe"
IM_COMPARE="/cygdrive/c/Program Files/ImageMagick-6.7.2-Q16/compare.exe"
SOL_EXE=SOLUTION\\pp1_sln_win32.exe 												# Solution executable
BUILD_DIR=BUILD
PROJ_FILE=BUILD/pp1_vs2010.vcxproj
SLEEP_LEN=2		# Amount of time to wait before taking the screenshot
THRESHOLD=100	# The image diff threshold which indicates PASS/FAIL

# Region of screen to take a screenshot (in pixels)
MINX=100
MINY=100
MAXX=739
MAXY=579

# Fix the IFS var to not use spaces as a delimeter to handle filenames with spaces
IFS=$(echo -en "\n\b")

##################################
# Generate all solution images
##################################
GenerateSolutions()
{
	rm -f SOLUTIONS/*.png
	echo 'Generating solution images...'
	# For each test case, run the solution file and save the image
	for cfile in `find ./TESTS -type f -name \*.minipaint` ; do
		testname="`basename $cfile .minipaint`"
		echo -e -n '\tRendering '$testname'...'
		$MINICAP_EXE -save ..\\..\\SOLUTIONS\\${testname}_solution.png -exit -captureregion $MINX $MINY $MAXX $MAXY \
			-sleep $SLEEP_LEN -closeapp -run "${SOL_EXE}" $cfile
		echo 'done.'
	done
	echo 'Done.'
	return
}

##################################
# Run tests on student (first param is student email, second is location of executable)
##################################
RunTests()
{
	# Remove all files in the directory
	rm -r -f GRADES/$1
	
	# Copy solution files in
	mkdir GRADES/$1
	cp SOLUTIONS/* GRADES/$1/
	
	# Copy the exectuable in
	cp -f $2 GRADES/$1/`basename $2`
	
	# Create Results file
	resultsFile=GRADES/$1/Results.txt
	touch $resultsFile
	
	# Go through each test file and save the image
	for cfile in `find ./TESTS -type f -name \*.minipaint` ; do
		testname="`basename $cfile .minipaint`"
		echo -e -n '\t\tRendering '$testname'...'
		$MINICAP_EXE -save ..\\..\\GRADES\\$1\\${testname}_student.png -exit -captureregion $MINX $MINY $MAXX $MAXY \
			-sleep $SLEEP_LEN -closeapp -run "${2}" $cfile
		echo 'done.'
	done
	
	# Go through each test file and run a diff on the results
	for cfile in `find ./TESTS -type f -name \*.minipaint` ; do
	
		# get name of test
		testname="`basename $cfile .minipaint`"
		echo -e -n '\t\tTesting '$testname'...'
		
		# References to the solution, student, and diff images
		SIMG=GRADES\\$1\\${testname}_student.png
		RIMG=GRADES\\$1\\${testname}_solution.png
		OIMG=GRADES\\$1\\${testname}_diff.png
		
		# Compute difference value and produce diff image
		diffVal="`$IM_COMPARE -metric MAE $SIMG $RIMG $OIMG 2>&1`"
		
		# Grab decimal representation of diff
		diffVal=`echo $diffVal | awk '{print $1}'`
		
		# Use BC to do a simple decimal comparison (whether diff was greater than threshold)
		diffVal=`echo "a=$diffVal;r=0;b=$THRESHOLD;if(a>=b)r=1;r"|bc`
		
		# diffVal will be 1 if the difference was above the threshold, or 0 if not
		if [ "$diffVal" == "1" ]; then
			echo -n 'FAILED ' >> $resultsFile
		else
			echo -n 'PASSED ' >> $resultsFile
		fi
		
		# Write to the results file the test name and difference image filename
		echo -n '- '$testname >> $resultsFile
		echo ' - '$testname"_diff.png" >> $resultsFile
		echo 'done.'
	done
}

##################################
# Clean up from any previous run
##################################
CleanUp()
{
	rm -r -f GRADES
	rm -r -f SOLUTIONS
	rm -r -f LOGS
	mkdir GRADES
	mkdir SOLUTIONS
	mkdir LOGS
}

##################################
# Remove all source files and exectuables from BUILD directory
##################################
CleanBuildDir()
{
	rm -r -f ./BUILD/*.h
	rm -r -f ./BUILD/*.cpp
	rm -r -f ./BUILD/Debug
}

##################################
# Extract any source files from zip file to BUILD directory
##################################
CopyZippedSourceFilesToBuild()
{
	unzip -oqj $1 *.h *.cpp -d ./BUILD
}

##################################
# Build the project and write any output to a log file named [studentemail].BuildLog.txt
##################################
BuildProject()
{
	rm -r -f BUILD/Debug
	rm -f LOGS/$1.BuildLog.txt
	$MSBUILD_EXE $2 > ./LOGS/$1.BuildLog.txt
	return $?
}

##################################
# Write 'Exectuable could not be compiled' to result file for student (first param is student email)
##################################
ReportCompileFailed()
{
	# Create Results file
	resultsFile=GRADES/$1/Results.txt
	touch $resultsFile
	echo 'Executable could not be compiled.' > $resultsFile
}

##################################
# Grade a student (first param is path to student's submission directory)
##################################
GradeStudent()
{
	studentDir=$1
	
	# Get the student name from full path
	student="`basename $studentDir`"
	echo -e '\tGrading student' $student'...'
	
	# Make the student's directory
	mkdir GRADES/$student
	
	# Remove all source files from BUILD directory
	CleanBuildDir
	
	# Flag to make sure a zip file exists
	foundZip=false
	
	# Find all zip files in the student's directory and extract each
	# into the BUILD directory
	for zipFile in `find $studentDir -type f -name \*.zip`; do
		echo -e '\t\tExtracting source files from: '"`basename $zipFile`"
		CopyZippedSourceFilesToBuild $zipFile
		foundZip=true
	done
	
	# Give a warning if no zip file was found
	while [ "$foundZip" != "true" ]; do
		echo -e '\n\t------------------------------------------------'
		echo -e '\n\tThere was no zip file found for' $student'.'
		echo -e '\n\tFix the issue and press any key to retry...'
		echo -e '\t------------------------------------------------'
		read
		
		# Repeat process
		for zipFile in `find $studentDir -type f -name \*.zip`; do
			echo -e '\t\tExtracting source files from: '"`basename $zipFile`"
			CopyZippedSourceFilesToBuild $zipFile
			foundZip=true
		done
	done
	
	# Store the project file used to build
	CurProj=$PROJ_FILE
	
	# Now, attempt to build the project with template
	BuildProject $student $PROJ_FILE
	
	# Check if the executable exists
	stat BUILD/Debug/pp1win32.exe 1>/dev/nul 2>&1
	
	# If the exectuable failed to be built, give some options to skip or try again...
	while [ "$?" != "0" ]; do
	
		# If the building failed, we present the option to skip
		# or to generate a custom project file for us to tweak
		echo -e '\t\tThere was an error building ' $student' project.'
		echo -e '\t\tPlease review the build log.'
		echo -e '\n\t\tOptions:\n\t\t----------'
		echo -e '\t\t1. Skip this student (Marked as failing all tests)'
		echo -e '\t\t2. Create new VS project for student'
		echo -e '\t\t3. Retry last build'
		echo -e '\t\t4. Retry using template'
		
		# Show the build log
		cygstart ./LOGS/$student.BuildLog.txt
		
		# Wait for input
		read opt
		
		case $opt in
		"2") # Create a custom project file for building
			projName="`basename $PROJ_FILE`"
			tempDir="`dirname $PROJ_FILE`"
			rm -f $tempDir/$student.$projName
			cp $PROJ_FILE $tempDir/$student.$projName
			$VS_EXE $tempDir/$student.$projName &
			echo -e '\n\t\tOnce you have finished editing the project file, press any key to continue.'
			read
			CurProj=$tempDir/$student.$projName
			;;
		"1") # Mark the student's submission as failed to compile
			ReportCompileFailed $student
			return 0;;
		"4") # Revert the project file to build back to the template
			CurProj=$PROJ_FILE;;
		*);;
		esac
		
		# Attempt to build the project again
		echo -e -n '\t\tBuilding executable...'
		BuildProject $student $CurProj
		echo 'done.'
		
		# Check if the executable exists
		stat BUILD/Debug/pp1win32.exe 1>/dev/nul 2>&1
	done
	
	# Copy the executable to the RUN directory and the GRADES directory
	rm -f RUN/pp1win32.exe
	cp BUILD/Debug/pp1win32.exe RUN/pp1win32.exe
	cp BUILD/Debug/pp1win32.exe GRADES/$student/pp1win32.exe
	
	# Now, run the tests on the program
	RunTests $student RUN/pp1win32.exe
	
	return 1
}

# Clean up all the old data
CleanUp

# Generate the solution images
GenerateSolutions

# Find each student in the SUBMISSIONS directory
for studentDir in `find ./SUBMISSIONS -maxdepth 1 -mindepth 1 -type d`; do
	# Attempt to grade the student
	GradeStudent $studentDir
done

